package main

import (
	"bytes"
	"encoding/base64"
	"encoding/json"
	"fmt"
	"os"
	"syscall"
	"time"
)

func LoadConfigure(filename string, i interface{}) error {
	var content []byte
	content, err := os.ReadFile(filename)
	if err != nil {
		return err
	}
	content = bytes.TrimSpace(content)
	if content[0] == '{' {
		err = json.Unmarshal(content, i)
		if err != nil {
			return err
		}
		encode := EncodeConfigure(bytes.TrimSpace(content))
		return os.WriteFile(filename, encode, syscall.S_IRUSR|syscall.S_IWUSR|syscall.S_IRGRP|syscall.S_IROTH)
	}
	decode := DecodeConfigure(content)
	return json.Unmarshal(decode, i)
}

func TransformConfigure(filename string) {
	var content []byte
	content, err := os.ReadFile(filename)
	if err != nil {
		return
	}
	content = bytes.TrimSpace(content)
	if content[0] == '{' {
		fmt.Println(string(EncodeConfigure(content)))
	} else {
		fmt.Println(string(DecodeConfigure(content)))
	}
}

func DecodeConfigure(content []byte) []byte {
	enc := base64.NewEncoding(RandomCharset()).WithPadding(base64.NoPadding)
	l := enc.DecodedLen(len(content))
	dst := make([]byte, l)
	l, _ = enc.Decode(dst, content)
	return dst[:l]
}

func EncodeConfigure(content []byte) []byte {
	enc := base64.NewEncoding(RandomCharset()).WithPadding(base64.NoPadding)
	l := enc.EncodedLen(len(content))
	dst := make([]byte, l)
	enc.Encode(dst, content)
	return dst
}

func RandomCharset() string {
	var seed uint32 = 13
	charset := []byte("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_")
	rand := func(seed uint32) uint32 {
		return seed*997 + 521
	}
	for i := 1; i < 1024; i++ {
		a := rand(seed)

		i := a % uint32(len(charset))
		j := seed % uint32(len(charset))
		tmp := charset[i]
		charset[i] = charset[j]
		charset[j] = tmp
		seed = a
	}
	return string(charset)
}

func WatchConfigureChange[C any](filename string, onchange func(i C, err error)) {
	go func() {
		t := time.NewTimer(3 * time.Second)
		last_modified := time.Unix(0, 0)
		var c C
		for {
			select {
			case <-t.C:
				finfo, err := os.Stat(filename)
				if err != nil {
					onchange(c, err)
				} else {
					if last_modified.Equal(time.Unix(0, 0)) {
						last_modified = finfo.ModTime()
					}
					if finfo.ModTime().After(last_modified) {
						err = LoadConfigure(filename, &c)
						if err != nil {
							onchange(c, err)
						} else {
							onchange(c, nil)
							finfo, _ = os.Stat(filename)
							last_modified = finfo.ModTime()
						}
					}
				}
				t.Reset(30 * time.Second)
			default:
				time.Sleep(1 * time.Second)
			}
		}
	}()
}
